﻿using Api.Common;
using Castle.DynamicProxy;
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Api.CoustomInterceptor
{
    public class ServiceCacheInterceptor : IInterceptor
    {
        private readonly RedisCache _redisCache;

        public ServiceCacheInterceptor(RedisCache redisCache)
        {
            _redisCache = redisCache;
        }
        public void Intercept(IInvocation invocation)
        {
            var method = invocation.MethodInvocationTarget ?? invocation.Method;
            var type = invocation.Method.ReturnType;
            //对当前方法的特性验证
            var qCachingAttribute = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CacheAttribute)) as CacheAttribute;
            if (qCachingAttribute != null)
            {
                //获取自定义缓存键
                var cacheKey = CustomCacheKey(invocation);
                var cacheValue = _redisCache.Get<dynamic>(cacheKey,out bool isExisted);
                if (isExisted)
                {
                    //将当前获取到的缓存值，赋值给当前执行方法
                    object response = cacheValue;
                    if (type != null && typeof(Task).IsAssignableFrom(type))
                    {
                        response = Task.FromResult(cacheValue);
                    }

                    invocation.ReturnValue = response;
                    return;
                }
                //去执行当前的方法
                invocation.Proceed();
                
                if (type.FullName == "System.Void")
                {
                    return;
                }
                //存入缓存
                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    object response;

                    //Type type = invocation.ReturnValue?.GetType();
                    
                    if (type != null && typeof(Task).IsAssignableFrom(type))
                    {
                        var genric = type.GenericTypeArguments;
                        if (genric.Count()==0)
                        {
                            return;
                        }
                        var resultProperty = type.GetProperty("Result");
                        response = resultProperty.GetValue(invocation.ReturnValue);
                    }
                    else
                    {
                        response = invocation.ReturnValue;
                    }
                    if (response != null)
                    {
                        var dis = new DistributedCacheEntryOptions();
                        dis.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(qCachingAttribute.AbsoluteExpiration);
                        if (qCachingAttribute.SlidingExpiration != 0)
                        {
                            dis.SlidingExpiration = TimeSpan.FromSeconds(qCachingAttribute.SlidingExpiration);
                        }
                        if (qCachingAttribute.AbsoluteExpirationRelativeToNow != 0)
                        {
                            dis.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(qCachingAttribute.AbsoluteExpirationRelativeToNow);
                        }
                        _redisCache.Set(cacheKey, response, dis);
                    }
                }
            }
            else
            {
                invocation.Proceed();//直接执行被拦截方法
            }
        }
        //自定义缓存键
        private string CustomCacheKey(IInvocation invocation)
        {
            var typeName = invocation.TargetType.Name;
            var methodName = invocation.Method.Name;
            var methodArguments = invocation.Arguments.Select(GetArgumentValue).ToList();//获取参数列表

            string key = $"{typeName}:{methodName}:";
            foreach (var param in methodArguments)
            {
                key += $"{param}:";
            }

            return key.TrimEnd(':');
        }
        //object 转 string
        private string GetArgumentValue(object arg)
        {
            if (arg == null)
            {
                return "";
            }
            // PS：这里仅仅是很简单的数据类型，如果参数是表达式/类等，比较复杂的，请看我的在线代码吧，封装的比较多，当然也可以自己封装。
            if (arg is int || arg is long || arg is string)
                return arg.ToString();

            if (arg is DateTime)
                return ((DateTime)arg).ToString("yyyyMMddHHmmss");
            var props = arg.GetType().GetProperties();
            var res = string.Empty;
            foreach (var prop in props)
            {
                res += $"{prop.GetValue(arg)}:";
            }
            return res;
        }
    }
    public class ServiceCacheAsyncInterceptor : IAsyncInterceptor
    {
        public void InterceptAsynchronous(IInvocation invocation)
        {

            throw new NotImplementedException();
        }

        public void InterceptAsynchronous<TResult>(IInvocation invocation)
        {
            throw new NotImplementedException();
        }

        public void InterceptSynchronous(IInvocation invocation)
        {
            throw new NotImplementedException();
        }
    }
}
